package com.ndp.fb.walle.business.impl;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import com.ndp.fb.rdb.model.ConnectionObject;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.ndp.ec.core.UserService;
import com.ndp.fb.config.ConfigCentre;
import com.ndp.fb.constant.CommonConstants;
import com.ndp.fb.constant.ConfigConst;
import com.ndp.fb.enums.connectionobject.ConnectionObjectType;
import com.ndp.fb.enums.ErrorType;
import com.ndp.fb.enums.adresource.ThirdPicReviewStatus;
import com.ndp.fb.model.FileBlock;
import com.ndp.fb.model.business.bo.nfs.NfsConfig;
import com.ndp.fb.model.business.vo.stats.AdResourceStatsQueryParam;
import com.ndp.fb.mongo.api.AdVideoMongoService;
import com.ndp.fb.mongo.model.AdvideoForMongo;
import com.ndp.fb.mongo.model.AdvideoSplit;
import com.ndp.fb.rdb.api.ProductManageRdbService;
import com.ndp.fb.rdb.api.UserRdbServices;
import com.ndp.fb.rdb.model.User;
import com.ndp.fb.rdb.model.productmanage.ProductManage;
import com.ndp.fb.redis.api.RedisDistributedLockService;
import com.ndp.fb.util.FileUtil;
import com.ndp.fb.util.ListUtil;
import com.ndp.fb.util.NFSUtil;
import com.ndp.fb.walle.business.AdVideoService;
import com.ndp.fb.walle.business.ConnectionObjectService;
import com.ndp.fb.walle.model.VideoMeta;
import com.ndp.fb.walle.model.bo.adresource.AdVideoBo;
import com.ndp.fb.walle.model.vo.adresource.AdResourceQueryParam;
import com.ndp.fb.walle.model.vo.adresource.AdResourceUpdateParam;
import com.ndp.fb.walle.util.FileUploadUtils;
import com.ndp.fb.walle.util.ResponseUtils;
import com.ndp.fb.walle.util.VideoUtils;

/**
 * Created by sisi.chen on 2016/1/15.
 */
@Component
public class AdVideoServiceImpl implements AdVideoService {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    private static final Set<String> RETURN_FIELDS = Sets.newHashSet("mongodbId", "resourceType", "name", "format", "size",
            "width", "height", "duration", "md5", "userId",
            "orgId", "nfsId", "liteNfsId", "splits", "tags",
            "uploadTime", "updateTime", "isDelete", "reviewStatus", "from");

    @Reference
    private UserRdbServices userRdbServices;

    @Reference
    private AdVideoMongoService adVideoMongoService;

    @Autowired
    private RedisDistributedLockService redisDistributedLockService;

    @Autowired
    private NfsConfig nfsConfig;

    @Reference
    private UserService userService;

    @Autowired
    private ConnectionObjectService connectionObjectService;

    @Reference
    private ProductManageRdbService productManageRdbService;

    @Autowired
    private NFSUtil nfsUtil;

    public Map<String, Object> getAdvideosList(AdResourceQueryParam adResourceQueryParam, User user) {
        AdResourceStatsQueryParam adResourceStatsQueryParam = buildBaseAdResourceStatsQueryParam(adResourceQueryParam, user);

        //分页信息
        adResourceStatsQueryParam.setPage(adResourceQueryParam.getCPage());
        adResourceStatsQueryParam.setPageSize(adResourceQueryParam.getPSize());

        //排序信息
        List<Map<String, Integer>> sortList = adResourceQueryParam.getSortList();
        if (sortList == null || sortList.size() == 0) {
            if (sortList == null) {
                sortList = new ArrayList<>();
            }
            Map<String, Integer> createTime = new HashMap<>();
            createTime.put("uploadTime", -1);
            sortList.add(createTime);
        }
        adResourceStatsQueryParam.setSortList(sortList);

        //期望返回的字段
        adResourceStatsQueryParam.setReturnFields(RETURN_FIELDS);
        logger.debug("查询参数>>>>" + com.alibaba.fastjson.JSON.toJSONString(adResourceStatsQueryParam));
        Map<String, Object> findByConditionsResult = adVideoMongoService.findByConditions(adResourceStatsQueryParam);

        if (null != findByConditionsResult && null != findByConditionsResult.get("adVideoList")) {
            List<AdvideoForMongo> advideos = JSON.parseArray(findByConditionsResult.get("adVideoList").toString(), AdvideoForMongo.class);
            List<User> users = userService.findByOrgId(user.getOrgId());
            Map<String, String> uMap = new HashMap<>();
            if (!CollectionUtils.isEmpty(users)) {
                for (User userT : users) {
                    uMap.put(userT.getTicket(), userT.getNickname());
                }
            }
            List<AdVideoBo> adVideoBos = new ArrayList<>();
            try {
                if (!CollectionUtils.isEmpty(advideos)) {
                    AdVideoBo adVideoBo;
                    for (AdvideoForMongo advideoForMongo : advideos) {
                        adVideoBo = new AdVideoBo();
                        convertAdvideo(adVideoBo, advideoForMongo);
                        adVideoBo.setUploadUserName(uMap.get(advideoForMongo.getUserId()));
                        adVideoBos.add(adVideoBo);
                    }
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
            findByConditionsResult.put("adVideoList", adVideoBos);
        }
        return findByConditionsResult;
    }

    private AdResourceStatsQueryParam buildBaseAdResourceStatsQueryParam(AdResourceQueryParam adResourceQueryParam, User user) {
        AdResourceStatsQueryParam adResourceStatsQueryParam = new AdResourceStatsQueryParam();
        adResourceStatsQueryParam.setResourceType(adResourceQueryParam.getResourceType());
        adResourceStatsQueryParam.setIsDelete(0); //没有删除

        //应用tag
        Map<String, Set<Object>> tags = new HashMap<>();
        if (ListUtil.isNotEmpty(adResourceQueryParam.getAppNames())) {
            tags.put("application", Sets.<Object>newHashSet(adResourceQueryParam.getAppNames().iterator()));
        }
        adResourceStatsQueryParam.setTags(tags);

        //公司
        adResourceStatsQueryParam.setOrgId(user.getOrgId());

        //第三方
        if (adResourceQueryParam.isReview()) {
            //获取该公司下，满足过滤条件的所有外部素材
            adResourceStatsQueryParam.setFrom(CommonConstants.IMG_FROM_OUTUI);  //从外部设计师过来的
            return adResourceStatsQueryParam;
        }

        //图片所属者
        if (!CollectionUtils.isEmpty(adResourceQueryParam.getUserIdSet())) {
            adResourceStatsQueryParam.setUserIdSet(adResourceQueryParam.getUserIdSet());
        }

        /**
         * 1、如果是外部美宣  则查所有审核状态
         * 2、如果是其他角色  则查所有审核通过的
         */
        List<Long> roleIds = userRdbServices.getRoleByUser(user.getId());;
        if (ListUtil.isNotEmpty(roleIds) && roleIds.contains(CommonConstants.ROLE_OUTERUIDESIGNER)) {
            logger.debug("AdImageServiceImpl buildBaseAdResourceStatsQueryParam() 当前用户是外部美宣。");
        } else {
            adResourceStatsQueryParam.setReviewStatus(ThirdPicReviewStatus.PERSON_REVIEW_PASS.getCode());
        }

        return adResourceStatsQueryParam;
    }

    @Override
    public  List<AdVideoBo> uploadAdvideo(List<String> appNames, String appId, List<MultipartFile> fileList, User user, String contextPath) {
        //遍历视频
        List<AdVideoBo> uploadResults = new ArrayList<>();
        for (MultipartFile multipartFile : fileList){
            AdVideoBo adVideoBo;
            String fileName =  multipartFile.getOriginalFilename();
            try {
                adVideoBo = uploadSingleVideo(appNames, user, contextPath, multipartFile.getBytes(), fileName);
            } catch (Exception e){
                logger.error(e.getMessage(), e);
                adVideoBo = new AdVideoBo();
                adVideoBo.setFileName(fileName);
                buildUploadResult(adVideoBo, 0, ErrorType.UPLOAD_RESOURCE_FAIL);
            }
            uploadResults.add(adVideoBo);
        }

        return uploadResults;
    }

    private AdVideoBo uploadSingleVideo(List<String> appNames, User user, String contextPath, byte[] fileBytes, String fileName) {
        File tmpFile = null;
        AdVideoBo adVideoBo = new AdVideoBo();
        adVideoBo.setFileName(fileName);
        String lockId = null;
        try {
            //tmpFile = FileUploadUtils.multipartFile2File(multipartFile, contextPath);
            tmpFile = FileUploadUtils.bytes2File(fileBytes, fileName, contextPath);
            String md5 = FileUtil.getFileMd5(tmpFile);
            lockId = "lock_" + md5 + "_" + user.getOrgId();
            if (!redisDistributedLockService.tryLock(lockId, 1, TimeUnit.MINUTES)) {
                //正在上传
                buildUploadResult(adVideoBo, 0, ErrorType.VIDEO_REPEAT_ERROR);
                return adVideoBo;
            }

            //同一个视频在同一个公司不能重复上传
            AdvideoForMongo advideoForMongo = adVideoMongoService.findByMd5AndOrgId(md5, user.getOrgId());
            if (null != advideoForMongo){
                //处理已经上传过的视频
                handleUploadedVideo(appNames, advideoForMongo);
            } else {
                //上传新视频
                advideoForMongo = uploadNewVideo(appNames, user, tmpFile, fileName, md5, contextPath);
            }

            //上传成功
            convertAdvideo(adVideoBo, advideoForMongo);
            adVideoBo.setUploadUserName(user.getNickname());
            buildUploadResult(adVideoBo, 1, null);
        } catch (Exception e){
            logger.error(e.getMessage(), e);
            buildUploadResult(adVideoBo, 0, ErrorType.UPLOAD_RESOURCE_FAIL);
        } finally {
            //删除视频
            if (null != tmpFile) {
                tmpFile.delete();
            }

            //解锁
            if (null != lockId){
                redisDistributedLockService.unLock(lockId);
            }

        }

        return adVideoBo;
    }

    private void buildUploadResult(AdVideoBo adVideoBo, Integer uploadStatus, ErrorType errorType) {
        adVideoBo.setUploadStatus(uploadStatus);
        if (null != errorType){
            adVideoBo.setErrorCode(errorType.getCode());
            adVideoBo.setMsg(ResponseUtils.getMsg(errorType));
        }
    }

    @Override
    public List<AdVideoBo> getAdvideoByIds(List<String> mongodIds) {
        List<AdvideoForMongo> advideoForMongos = adVideoMongoService.findByMongoIds(mongodIds);
        List<AdVideoBo> adVideoBos = new ArrayList<>();
        try {
            if (!CollectionUtils.isEmpty(advideoForMongos)){
                AdVideoBo adVideoBo;
                for (AdvideoForMongo advideoForMongo : advideoForMongos){
                    adVideoBo = new AdVideoBo();
                    convertAdvideo(adVideoBo, advideoForMongo);
                    adVideoBos.add(adVideoBo);
                }
            }
        } catch (Exception e){
            logger.error(e.getMessage(), e);
        }

        return adVideoBos;
    }

    private AdVideoBo convertAdvideo(AdVideoBo adVideoBo, AdvideoForMongo advideoForMongo) throws Exception{
        BeanUtils.copyProperties(adVideoBo, advideoForMongo);
        adVideoBo.setVisitPath(nfsConfig.getFullAddress() + CommonConstants.SYMBOL_SLASH + advideoForMongo.getNfsId());
        adVideoBo.setLitImgURL(nfsConfig.getFullAddress() + CommonConstants.SYMBOL_SLASH + advideoForMongo.getLiteNfsId());
        return adVideoBo;
    }

    @Override
    public void updateAdvideos(List<AdResourceUpdateParam> adResourceUpdateParams, User user) {
        //获取用户有权限的应用名称
        List<String> appsList = getAuthAppNames(user);

        //遍历待更新的视频
        for (AdResourceUpdateParam adResourceUpdateParam : adResourceUpdateParams){
            AdvideoForMongo advideoForMongo = adVideoMongoService.findByMongoId(adResourceUpdateParam.getMongodbId());
            List<Object> oldAppNames = null != advideoForMongo.getTags() && null != advideoForMongo.getTags().get("application") ? advideoForMongo.getTags().get("application") : null;
            List<String> appNames = resetAppNameTags(appsList, adResourceUpdateParam.getAppNames(), oldAppNames);
            adResourceUpdateParam.setAppNames(appNames);

            Map<String, Object> updateFields = new HashMap<>();
            if (ListUtil.isNotEmpty(appNames)) {
                updateFields.put("tags.application", appNames);
            }

            if (null != adResourceUpdateParam.getLanguages()) {
                updateFields.put("tags.languages", adResourceUpdateParam.getLanguages());
            }

            if (null != adResourceUpdateParam.getGender()) {
                updateFields.put("tags.gender", Arrays.asList(adResourceUpdateParam.getGender()));
            }


            if (StringUtils.isNotEmpty(adResourceUpdateParam.getName())){
                updateFields.put("name", adResourceUpdateParam.getName());
            }

            adVideoMongoService.update(adResourceUpdateParam.getMongodbId(), updateFields);
        }
    }

    private List<String> getAuthAppNames(User user) {
        //获取应用信息
        String tmpUserid;
        List<ConnectionObject> connectionObjectBoList;
        List<Long> roleIds = userService.getRoleByUser(user.getId());
        if (ListUtil.isNotEmpty(roleIds) && (roleIds.contains(CommonConstants.ROLE_UIDESIGNER) || roleIds.contains(CommonConstants.ROLE_OUTERUIDESIGNER))) {
            //设计者获取该公司下有权限的应用
            connectionObjectBoList = connectionObjectService.findByOrgIdAndType(user.getOrgId(), ConnectionObjectType.Application.getType());
        } else {
            //非美宣，查询各自有权限的应用
            connectionObjectBoList = connectionObjectService.findByUserIdAndType(user.getTicket(), ConnectionObjectType.Application.getType());
        }

        List<String> appsList = new ArrayList<>();
        if (connectionObjectBoList != null) {
            for (int i = 0; i < connectionObjectBoList.size(); i++) {
                ConnectionObject connectionObjectBo = connectionObjectBoList.get(i);
                appsList.add(connectionObjectBo.getName());
            }
        }

        //外链
        List<ProductManage> productManages = productManageRdbService.findByOrgId(user.getOrgId());
        if (!CollectionUtils.isEmpty(productManages)){
            for (ProductManage productManage : productManages){
                if (!appsList.contains(productManage.getProdName())){
                    appsList.add(productManage.getProdName());
                }
            }
        }
        return appsList;
    }

    /**
     * 重置应用名称Tag
     * @param authAppNames
     * @param inputAppNamesParams
     * @param oldAppNames
     * @return
     */
    private List<String> resetAppNameTags(List<String> authAppNames, List<String> inputAppNamesParams, List<Object> oldAppNames) {
        List<String> newAppNames = new ArrayList<>();
        if (ListUtil.isNotEmpty(oldAppNames)) {
            for (Object o : oldAppNames) {
                newAppNames.add(o.toString());
            }
        }

        //只能删除用户有权限的应用Tag
        for (String dbAppName : authAppNames) {
            newAppNames.remove(dbAppName);
        }

        //添加用户新增的tag
        if (!CollectionUtils.isEmpty(inputAppNamesParams)){
            newAppNames.addAll(inputAppNamesParams);
        }

        return newAppNames;
    }

    private void handleUploadedVideo(List<String> appName, AdvideoForMongo advideoForMongo) {
        //处理已上传过的视频
        if (null != advideoForMongo.getIsDelete() && 1 ==  advideoForMongo.getIsDelete()){
            //清空原来的Tag
            advideoForMongo.setTags(null);
            //修改上传时间
            advideoForMongo.setUploadTime((new Date()).getTime());
        }

        Map<String, List<Object>> saveTags;
        Map<String, List<Object>> tmpTags = advideoForMongo.getTags();
        if (null != tmpTags){
            //如果之前有Tag就用之前的Tag
            saveTags = tmpTags;
        } else {
            saveTags = Maps.newHashMap();
        }

        if(!CollectionUtils.isEmpty(saveTags.get("application"))){
            //在原有应用的基础上继续添加
            List<Object> apps = saveTags.get("application");
            Set appSet = Sets.newHashSet(apps);
            appSet.addAll(appName);
            saveTags.put("application", Lists.newArrayList(appSet));
        } else {
            List apps = Lists.newArrayList();
            apps.addAll(appName);
            saveTags.put("application",apps);
        }

        //更新视频信息
        Map<String,Object> map = Maps.newHashMap();
        map.put("isDelete",0);
        map.put("tags",saveTags);
        map.put("updateTime", (new Date()).getTime());
        map.put("uploadTime", advideoForMongo.getUploadTime());
        advideoForMongo.setTags(saveTags);
        adVideoMongoService.update(advideoForMongo.getMongodbId(), map);
    }

    private AdvideoForMongo uploadNewVideo(List<String> appNames, User user, File tmpFile, String fileName, String md5, String contextPath) throws Exception {
        //1、解析视频获取基本信息：时长、分辨率、缩略图
        VideoMeta videoMeta = VideoUtils.captureFirstFrame(tmpFile, contextPath + File.separator + "upload", "jpg");

        //2、视频上传到nfs
        String nfsId = nfsUtil.uploadNfsFile(tmpFile);

        //3、将截取的缩略图上传到nfs
        String liteNfsId = nfsUtil.uploadNfsFile(videoMeta.getImg());

        if (null != videoMeta.getImg()){
            //删除临时缩略图
            videoMeta.getImg().delete();
        }

        //4、视频分块上传到nfs上
        List<AdvideoSplit> splits = blockFile(tmpFile);

        //5、构造存储对象
        AdvideoForMongo advideoForMongo = new AdvideoForMongo();
        advideoForMongo.setMongodbId(md5 + "_" + user.getOrgId());
        advideoForMongo.setMd5(md5);
        advideoForMongo.setName(fileName);
        advideoForMongo.setResourceType("VIDEO");
        advideoForMongo.setSize(tmpFile.length());
        advideoForMongo.setFormat(fileName.substring(fileName.lastIndexOf(".") + 1));
        advideoForMongo.setWidth(videoMeta.getWidth());
        advideoForMongo.setHeight(videoMeta.getHeight());
        advideoForMongo.setDuration(videoMeta.getDuration());
        advideoForMongo.setUserId(user.getTicket());
        advideoForMongo.setOrgId(user.getOrgId());
        advideoForMongo.setNfsId(nfsId);
        advideoForMongo.setLiteNfsId(liteNfsId);
        advideoForMongo.setIsDelete(0);
        //默认为人工审核状态
        advideoForMongo.setReviewStatus(ThirdPicReviewStatus.PERSON_REVIEW_PASS.getCode());
        //默认来自内部
        advideoForMongo.setFrom(CommonConstants.IMG_FROM_INNERUI);
        advideoForMongo.setSplits(splits);
        //tag
        /// appName tag
        ArrayList strings = Lists.newArrayList(appNames);
        Map<String, List<Object>> apps = new HashMap<>();
        apps.put("application", strings);
        advideoForMongo.setTags(apps);
        Long t = (new Date()).getTime();
        advideoForMongo.setUploadTime(t);
        advideoForMongo.setUpdateTime(t);

        //存储视频信息
        adVideoMongoService.batchSave(Arrays.asList(advideoForMongo));

        return advideoForMongo;
    }

    private List<AdvideoSplit> blockFile(File tmpFile) throws Exception {
        List<FileBlock> blockList = FileUtil.SplitBySize(tmpFile, ConfigCentre.getInteger(ConfigConst.SPLIT_UPLOAD_VIDEO_BLOCK_SIZE));
        List<AdvideoSplit> splits = new ArrayList<>();
        String splitFileId;
        for (FileBlock fileBlock : blockList) {
            //块上传到nfs
            splitFileId = nfsUtil.uploadNfsFile(fileBlock.getBlockfile());
            splits.add(new AdvideoSplit(splitFileId, fileBlock.getMd5(), fileBlock.getStartPosition(), fileBlock.getEndPosition()));
            //删除临时块
            if(!fileBlock.getBlockfile().getName().equals(tmpFile.getName())){
                FileUtil.deleteQuietly(fileBlock.getBlockfile());
            }
        }

        return splits;
    }

	@Override
	public AdVideoBo getAdvideoByFbVideoId(String fbVideoId) {
		AdvideoForMongo advideoForMongo = adVideoMongoService.findByFbVideoId(fbVideoId);
		AdVideoBo adVideoBo = new AdVideoBo();
        try {
        	if(advideoForMongo==null){
        		return null;
        	}
			convertAdvideo(adVideoBo, advideoForMongo);
			return adVideoBo;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
}
